#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# -*- mode: python -*-

from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

def parse_op_code_h(path):
    import io
    import re
    MARKER = '/* Instruction opcodes for compiled code */'
    PATTERN = '#define ([A-Z_]+) +([0-9]+)'
    with io.open(path, 'r', encoding='utf-8', newline='\n') as fin:
        for line in fin:
            if MARKER in line:
                break
        pattern = re.compile(PATTERN)
        op_codes = []
        for line in fin:
            match = pattern.match(line)
            if match is None:
                break
            name = match.group(1)
            value = int(match.group(2))
            if name != 'HAVE_ARGUMENT':
                op_code = name, value
                op_codes.append(op_code)
        return tuple(op_codes)

def to_pascal_name(name):
    string = ''
    for item in name.split('_'):
        string += item[0].upper() + item[1:].lower()
    return string

def format_op_codes(op_codes):
    HAVE_ARGUMENT = 90
    string = ''
    for name, _ in op_codes:
        if string:
            string += '\n'
        string += "%%token '%s'" % name.lower()
    string += '\n'
    string += '\nInstruction:'
    for name, value in op_codes:
        string += '\n'
        string += '    %s' % to_pascal_name(name)
        if value >= HAVE_ARGUMENT:
            string += ' (argument : StringLiteral)'
    string += '\n'
    string += '\n%%'
    string += '\n'
    string += '\nInstruction as Instruction:'
    for name, value in op_codes:
        string += '\n'
        string += "    '%s'" % name.lower()
        if value >= HAVE_ARGUMENT:
            string += ' StringLiteral'
        string += ' => Instruction.New%s(' % to_pascal_name(name)
        if value >= HAVE_ARGUMENT:
            string += '$2, '
        string += '@@)'
    return string

def output_op_codes_text(string, path):
    import io
    with io.open(path, 'w', encoding='utf-8-sig', newline='\r\n') as fout:
        print(string, file=fout)

def main():
    import os
    path = os.path.dirname(__file__)
    path = os.path.join(path, 'cpython', 'opcode-3.9.h')
    op_codes = parse_op_code_h(path)
    string = format_op_codes(op_codes)
    path, _ = os.path.splitext(__file__)
    path += '.txt'
    output_op_codes_text(string, path)

if __name__ == '__main__':
    main()
